План объёмный (~4 часа лекции). Рекомендуется темп: разделы 1-5 — первое занятие, 6-10 — второе. Обратить внимание студентов на привязку к экзаменационным вопросам 31-40.
Не углубляться в историю — дать краткую сводку. Ключевое: Linux — не UNIX (нет лицензии), но POSIX-совместимый. BSD — настоящая UNIX-ветвь. macOS — сертифицированный UNIX (Darwin).
POSIX определяет API, а не реализацию. Linux реализует большую часть POSIX, но не сертифицирован. Windows имеет подсистему POSIX (устаревшую) и WSL.
Обратить внимание на x — пароли хранятся в /etc/shadow. UID 0 = root. Системные аккаунты: UID 1-999 (обычно), пользовательские: UID 1000+.
Ключевой момент: setuid-программы (например, sudo, passwd) имеют real UID пользователя, но effective UID = root. Именно effective UID проверяется при доступе к файлам.
Аналогия: syscall — заказ в ресторане через официанта (контекстное переключение), библиотечная функция — приготовление дома (без обращения к ядру).
Важно: errno не обнуляется при успешном вызове. Проверять нужно возвращаемое значение, а затем errno. Потокобезопасность: errno — thread-local переменная.
.text — только для чтения и исполнение, .data — чтение/запись, .bss — нулевые байты при загрузке (не занимает места в файле). .symtab используется при отладке и динамической компоновке.
`file` — определяет тип файла. `readelf -h` — заголовок (архитектура, точка входа). `nm` — список символов. PE — Portable Executable, развитие COFF.
Обратить внимание на числа в столбце размера для устройств: старший номер (драйвер) и младший номер (экземпляр). /dev/null — «чёрная дыра», устройства — в /dev.
`lstat` — не следует по символическим ссылкам (в отличие от `stat`). Макросы S_IS* — предпочтительный способ проверки типа.
Классическая модель. 755 — стандарт для исполняемых файлов, 644 — для обычных файлов. Обратить внимание: каталог без x нельзя войти (cd), без r нельзя прочитать содержимое (ls).
umask — глобальная настройка оболочки. 0022 — стандартное значение для большинства систем. chmod с числами — восьмеричная система.
passwd — классический пример setuid: обычный пользователь выполняет программу с правами root для записи в /etc/shadow. Sticky bit на /tmp предотвращает удаление чужих файлов. Без sticky bit любой пользователь мог бы удалить файлы в /tmp.
ACL — расширение POSIX. Файловые системы ext4, XFS, Btrfs поддерживают ACL. При наличии ACL в ls -l появляется символ '+'. Использовать ACL когда стандартной модели rwx недостаточно.
atime — время последнего чтения (может быть отключено через mount option noatime). mtime — время изменения содержимого. ctime — время изменения метаданных (прав, владельца). Разница ctime и mtime — частый вопрос на экзамене.
open() возвращает файловый дескриптор (int ≥ 0) или -1 при ошибке. 0, 1, 2 — стандартные потоки (stdin, stdout, stderr). dup2 — перенаправление — основа для реализации shell-пайпов.
access() проверяет права от имени real UID (не effective). ioctl() — «швейцарский нож» для специфичных операций с устройствами. fcntl() — управление файловым дескриптором (неблокирующий I/O, блокировки).
Superblock — «паспорт» файловой системы, считывается при монтировании. Inode — метаданные файла (всё, кроме имени). Имя хранится в каталоге как пара (имя → номер inode). Жёсткая ссылка — дополнительное имя для того же inode.
ext4 — стандартная ФС большинства дистрибутивов Linux. Btrfs и ZFS — «следующее поколение» с встроенным управлением томами. df — свободное место, du — использование каталога.
Демоны — от "disk and execution monitor". Обычно имя демона заканчивается на 'd': sshd, httpd, systemd. Демоны не имеют управляющего терминала.
Каждый процесс наследует атрибуты от родителя при fork(). PPID = 1 означает, что родитель завершился и процесс усыновлён init/systemd.
/proc — «окно» в ядро. cmdline — командная строка запуска (аргументы через \0). environ — переменные окружения. fd/ — символические ссылки на открытые файлы. maps — карта памяти процесса.
fork() возвращает 0 в дочернем процессе, PID дочернего — в родительском, -1 — при ошибке. Copy-on-write: страницы памяти разделяются до первой записи. Это делает fork() очень быстрым.
CoW — ключевая оптимизация: не копировать всю память сразу. Ядро помечает страницы как read-only. При записи генерируется page fault, ядро копирует страницу.
execve — единственный системный вызов, остальные — библиотечные обёртки. При успехе exec не возвращается (старый процесс полностью заменён). При ошибке возвращает -1.
wait() — ждёт любого дочернего. waitpid() — конкретного (или любого, с WNOHANG для неблокирующего ожидания). Без wait() дочерний процесс становится зомби.
Зомби занимает запись в таблице процессов. Если родитель не вызывает wait(), зомби накапливаются → исчерпание PID. Решение: 1) родитель вызывает wait(), 2) родитель игнорирует SIGCHLD (signal(SIGCHLD, SIG_IGN)), 3) kill родителя → зомби усыновляются init и очищаются.
nice — «уступчивость» процесса. Чем выше значение, тем больше процесс «уступает» другие. По умолчанию nice = 0. nice() и setpriority() эквивалентны.
SIGKILL и SIGSTOP нельзя перехватить, игнорировать или заблокировать. SIGTERM — «вежливый» способ завершения (программа может перехватить и завершиться корректно). SIGINT = Ctrl+C.
kill — название историческое, можно отправить любой сигнал, не только завершение. kill(0, sig) отправляет сигнал всем процессам в группе. raise — отправка сигнала самому себе.
sigaction() предпочтительнее: 1) SA_RESTART — перезапуск прерванных системных вызовов, 2) SA_SIGINFO — дополнительная информация (PID отправителя, значение), 3) поведение signal() различается между UNIX-системами. В обработчике безопасно использовать только async-signal-safe функции (write, _exit, но НЕ printf, malloc).
sigprocmask — управление маской блокировки. SIG_BLOCK — добавить к маске, SIG_UNBLOCK — убрать, SIG_SETMASK — заменить. Сигнал доставляется при разблокировке. SIGKILL и SIGSTOP заблокировать нельзя.
pipe() создаёт однонаправленный канал: pipefd[0] — чтение, pipefd[1] — запись. Важно закрывать неиспользуемые концы! Если записывающий конец закрыт — read() вернёт 0 (EOF). Если читающий конец закрыт — write() вызовет SIGPIPE.
FIFO — файл на диске типа 'p'. open() блокирует, пока другой процесс не откроет с противоположной стороны. Неблокирующий режим: O_NONBLOCK. В отличие от pipe(), FIFO существуют в файловой системе и могут использоваться несвязанными процессами.
System V IPC — старый, но широко поддерживаемый механизм. Ключи IPC — целые числа (обычно генерируются через ftok()). Команды ipcs/ipcrm — аналоги ls/rm для IPC-объектов.
mtype > 0 — тип сообщения, используется для выборочного чтения. msgrcv с mtype=0 — читать первое сообщение любого типа. msgsnd/msgrcv блокируют при полной/пустой очереди (если не указан IPC_NOWAIT).
semget(key, nsems, flags) — создание набора семафоров. semop — атомарная группа операций. sem_flg = SEM_UNDO — автоматический откат при завершении процесса (защита от мёртвых блокировок).
shmget — создание сегмента. shmat — подключение к адресному пространству (возвращает указатель). shmdt — отключение. shmctl IPC_RMID — удаление (сегмент удаляется после отключения всех процессов).
POSIX IPC использует имена вместо ключей (начинаются с '/'). mq_* — более удобный API чем System V msgsnd/msgrcv с поддержкой приоритетов и уведомлений. shm_open + mmap — современный подход к разделяемой памяти.
pthread_create — создание потока. pthread_join — ожидание завершения (аналог waitpid для потоков). Потоки разделяют адресное пространство, файловые дескрипторы и signal disposition, но имеют собственные стеки, thread ID и errno.
pthread_detach — автоматическая очистка при завершении (не нужно join). Thread-specific data — аналог TLS (Thread Local Storage), каждый поток имеет своё значение. cleanup_push/pop — безопасное освобождение ресурсов при отмене потока.
Подчеркнуть связность: файлы → процессы (fork/exec) → IPC (pipes из fork) → синхронизация (семафоры) → потоки (pthread). Всё строится на фундаменте системных вызовов.